#include <windows.h>
#include <commdlg.h>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <fstream>
#include <string>
#include <iostream>
#include <cctype>
#include <shellapi.h>
#include <vector>
#include <cwctype>
#include "resource.h"

using namespace std;

// 全局变量
wstring filename;
bool isPlaying = false;
bool isPaused = true;
int delayMs = 0;
thread playbackThread;
thread controlThread;
mutex fileMutex;
condition_variable cv;
bool stopPlayback = false;
HWND hWndMain;
HWND hEditStatus;
HWND hBtnSelect;
HWND hBtnStartStop;
HWND hBtnPauseResume;
HWND hEditDelay;
HWND hBtnMode;  // 新增模式按钮
HWND hBtnTopMost;  // 新增置顶按钮
HFONT hFont = NULL; // 添加全局字体句柄
bool isTopMost = false;  // 置顶状态

enum Mode { ANKE, GUGUA };
Mode currentMode = ANKE;  // 默认安可谱

// 函数声明
void SelectFile();
void PlaybackProc();
void ControlProc();
void UpdateStatus(const wstring& msg);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
bool IsUserAdmin();
bool RunAsAdmin(const wstring& executablePath);
wstring GetExecutablePath();
wstring ToWide(const string& str);

wstring ToWide(const string& str) {
    int len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0);
    if (len == 0) return L"";
    wstring wstr(len - 1, 0);
    MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &wstr[0], len);
    return wstr;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    try {
        wstring executablePath = GetExecutablePath();
        if (!IsUserAdmin()) {
            if (!RunAsAdmin(executablePath)) {
                return 1;
            }
            return 0;
        }
    }
    catch (const exception& e) {
        wstring wideMsg = ToWide(e.what());
        MessageBox(NULL, wideMsg.c_str(), L"Error", MB_OK | MB_ICONERROR);
        return 1;
    }

    // 注册窗口类
    WNDCLASSEX wc = { 0 };
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszClassName = L"PianoPlayerClass";
    wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
    wc.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
    RegisterClassEx(&wc);

    // 创建主窗口（稍增大高度以容纳新按钮）
    hWndMain = CreateWindow(L"PianoPlayerClass", L"Anke Player", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 400, 350, NULL, NULL, hInstance, NULL);

    if (!hWndMain) {
        MessageBox(NULL, L"窗口创建失败！", L"错误！", MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    // 创建仿宋体字体
    hFont = CreateFontW(
        16,                         // 字体高度（逻辑单位，约12pt）
        0,                          // 字体宽度（0表示自动）
        0,                          // 倾斜角度
        0,                          // 基线角度
        FW_NORMAL,                  // 字体粗细
        FALSE,                      // 斜体
        FALSE,                      // 下划线
        FALSE,                      // 删除线
        GB2312_CHARSET,             // 字符集（支持中文）
        OUT_DEFAULT_PRECIS,         // 输出精度
        CLIP_DEFAULT_PRECIS,        // 裁剪精度
        DEFAULT_QUALITY,            // 输出质量
        DEFAULT_PITCH | FF_DONTCARE, // 字体间距和家族
        L"SimSun"                   // 字体名称：仿宋体
    );

    if (!hFont) {
        MessageBox(NULL, L"创建字体失败！", L"错误！", MB_OK | MB_ICONERROR);
    }

    // 创建控件（第一行按钮，增加长度到110）
    hBtnSelect = CreateWindow(L"BUTTON", L"选择文件", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
        10, 10, 110, 30, hWndMain, (HMENU)1, hInstance, NULL);
    if (hBtnSelect) SendMessage(hBtnSelect, WM_SETFONT, (WPARAM)hFont, TRUE);

    hBtnStartStop = CreateWindow(L"BUTTON", L"开始", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
        130, 10, 110, 30, hWndMain, (HMENU)2, hInstance, NULL);
    if (hBtnStartStop) SendMessage(hBtnStartStop, WM_SETFONT, (WPARAM)hFont, TRUE);

    hBtnPauseResume = CreateWindow(L"BUTTON", L"暂停", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
        250, 10, 110, 30, hWndMain, (HMENU)3, hInstance, NULL);
    if (hBtnPauseResume) SendMessage(hBtnPauseResume, WM_SETFONT, (WPARAM)hFont, TRUE);

    // 第二行：延迟标签 + 编辑 + 模式按钮 + 置顶按钮
    HWND hStaticDelay = CreateWindow(L"STATIC", L"延迟 (ms):", WS_VISIBLE | WS_CHILD,
        10, 50, 80, 20, hWndMain, NULL, hInstance, NULL);
    if (hStaticDelay) SendMessage(hStaticDelay, WM_SETFONT, (WPARAM)hFont, TRUE);

    hEditDelay = CreateWindow(L"EDIT", L"100", WS_VISIBLE | WS_CHILD | WS_BORDER | ES_NUMBER,
        100, 50, 50, 20, hWndMain, NULL, hInstance, NULL);
    if (hEditDelay) SendMessage(hEditDelay, WM_SETFONT, (WPARAM)hFont, TRUE);

    hBtnMode = CreateWindow(L"BUTTON", L"安可谱", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
        160, 50, 80, 20, hWndMain, (HMENU)4, hInstance, NULL);  // 新增模式按钮，ID=4
    if (hBtnMode) SendMessage(hBtnMode, WM_SETFONT, (WPARAM)hFont, TRUE);

    hBtnTopMost = CreateWindow(L"BUTTON", L"置顶窗口", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
        250, 50, 80, 20, hWndMain, (HMENU)5, hInstance, NULL);  // 新增置顶按钮，ID=5
    if (hBtnTopMost) SendMessage(hBtnTopMost, WM_SETFONT, (WPARAM)hFont, TRUE);

    // 状态编辑框（调整高度）
    hEditStatus = CreateWindow(L"EDIT", L"", WS_VISIBLE | WS_CHILD | WS_BORDER | WS_VSCROLL | ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL,
        10, 80, 360, 220, hWndMain, NULL, hInstance, NULL);
    if (hEditStatus) SendMessage(hEditStatus, WM_SETFONT, (WPARAM)hFont, TRUE);

    ShowWindow(hWndMain, nCmdShow);
    UpdateWindow(hWndMain);
    SetForegroundWindow(hWndMain);  // 将窗口设置为前台窗口

    // 启动控制线程
    controlThread = thread(ControlProc);
    controlThread.detach();

    // 消息循环
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    // 清理字体资源
    if (hFont) DeleteObject(hFont);

    // 清理
    stopPlayback = true;
    if (playbackThread.joinable()) playbackThread.join();

    return static_cast<int>(msg.wParam);
}

// 窗口过程
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    case WM_COMMAND:
        switch (LOWORD(wParam)) {
        case 1:  // 选择文件
            if (!isPlaying) {
                SelectFile();
            }
            else {
                UpdateStatus(L"播放中无法选择文件！");
            }
            break;
        case 2:  // 开始/停止
            if (isPlaying) {
                stopPlayback = true;
                SetWindowText(hBtnStartStop, L"开始");
                UpdateStatus(L"已停止播放。");
                isPlaying = false;
            }
            else {
                if (filename.empty()) {
                    UpdateStatus(L"请先选择文件！");
                    break;
                }
                wchar_t delayStr[10];
                GetWindowText(hEditDelay, delayStr, 10);
                delayMs = _wtoi(delayStr);
                if (delayMs <= 0) {
                    UpdateStatus(L"无效的延迟值！");
                    break;
                }
                stopPlayback = false;
                isPaused = false;
                isPlaying = true;
                SetWindowText(hBtnStartStop, L"停止");
                UpdateStatus(L"开始播放... 模式: " + wstring(currentMode == ANKE ? L"安可谱" : L"呱呱谱"));
                if (playbackThread.joinable()) playbackThread.join();
                playbackThread = thread(PlaybackProc);
                playbackThread.detach();
            }
            break;
        case 3:  // 暂停/继续
            if (isPlaying) {
                isPaused = !isPaused;
                SetWindowText(hBtnPauseResume, isPaused ? L"继续" : L"暂停");
                UpdateStatus(isPaused ? L"已暂停" : L"已继续");
                cv.notify_all();
            }
            break;
        case 4:  // 模式切换
            currentMode = (currentMode == ANKE) ? GUGUA : ANKE;
            SetWindowText(hBtnMode, currentMode == ANKE ? L"安可谱" : L"呱呱谱");
            UpdateStatus(L"切换到 " + wstring(currentMode == ANKE ? L"安可谱" : L"呱呱谱") + L" 模式");
            break;
        case 5:  // 置顶/取消置顶
            if (isTopMost) {
                SetWindowPos(hWndMain, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
                SetWindowText(hBtnTopMost, L"置顶窗口");
                UpdateStatus(L"已取消窗口置顶");
                isTopMost = false;
            }
            else {
                SetWindowPos(hWndMain, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
                SetWindowText(hBtnTopMost, L"取消置顶");
                UpdateStatus(L"窗口已置顶");
                isTopMost = true;
            }
            break;
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    return 0;
}

void SelectFile() {
    OPENFILENAME ofn = { 0 };
    wchar_t fileBuf[MAX_PATH] = { 0 };
    ofn.lStructSize = sizeof(OPENFILENAME);
    ofn.hwndOwner = hWndMain;
    ofn.lpstrFile = fileBuf;
    ofn.nMaxFile = MAX_PATH;
    ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
    ofn.lpstrFilter = L"文本文件\0*.txt\0所有文件\0*.*\0";

    if (GetOpenFileName(&ofn)) {
        filename = fileBuf;
        UpdateStatus(L"已选择文件: " + filename);

        wifstream file(filename);
        if (file.is_open()) {
            wstring line;
            if (getline(file, line) && !line.empty()) {
                try {
                    int fileDelay = stoi(line);
                    if (fileDelay > 0) {
                        wstring delayStr = to_wstring(fileDelay);
                        SetWindowText(hEditDelay, delayStr.c_str());
                        UpdateStatus(L"从文件加载延迟: " + delayStr + L" 毫秒");
                    }
                    else {
                        UpdateStatus(L"文件中延迟值无效，请手动输入。");
                        SetWindowText(hEditDelay, L"100");
                    }
                }
                catch (...) {
                    UpdateStatus(L"无法解析文件中的延迟值，请手动输入。");
                    SetWindowText(hEditDelay, L"100");
                }
            }
            else {
                UpdateStatus(L"文件中未找到延迟值，请手动输入。");
                SetWindowText(hEditDelay, L"100");
            }
            file.close();
        }
        else {
            UpdateStatus(L"无法打开文件以读取延迟，请手动输入。");
            SetWindowText(hEditDelay, L"100");
        }

        // 添加提醒消息
        UpdateStatus(L"提醒：按 End 键开始/停止，按 Home 键暂停/继续，按 PgUp 键选择文件。当前模式: " + wstring(currentMode == ANKE ? L"安可谱" : L"呱呱谱"));
    }
}

void PlaybackProc() {
    wifstream file(filename);
    if (!file.is_open()) {
        UpdateStatus(L"无法打开文件！");
        isPlaying = false;
        return;
    }

    wstring line;
    getline(file, line);  // 跳过第一行（延迟行）

    wchar_t c;
    int currentDelay = delayMs;

    if (currentMode == ANKE) {
        // 安可谱模式：原有逻辑
        UpdateStatus(L"使用延迟: " + to_wstring(delayMs) + L" 毫秒 (安可谱)");

        while (!stopPlayback && file.get(c)) {
            unique_lock<mutex> lock(fileMutex);
            cv.wait(lock, [&] { return !isPaused || stopPlayback; });
            if (stopPlayback) break;

            // 遇到 '#' 或中文，跳过本行
            if (c == L'#' || (c >= L'\u4E00' && c <= L'\u9FFF')) {
                while (file.get(c) && c != L'\n');
                continue;
            }

            if (c == L'(') currentDelay = 0;
            else if (c == L'[') currentDelay = delayMs / 2;
            else if (c == L'{') currentDelay = delayMs / 4;
            else if (c == L'<') currentDelay = delayMs / 16;
            else if (c == L')' || c == L']' || c == L'}' || c == L'>') currentDelay = delayMs;
            else if (iswupper(c)) {
                BYTE vk = (BYTE)c;
                BYTE scan = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
                keybd_event(vk, scan, 0, 0);
                keybd_event(vk, scan, KEYEVENTF_KEYUP, 0);
            }

            if (c != L'\n' && c != L'/' && c != L'(' && c != L')' && c != L'[' && c != L']' && c != L'{' && c != L'}' && c != L'<' && c != L'>') {
                this_thread::sleep_for(chrono::milliseconds(currentDelay));
            }
        }
    }
    else {
        // 呱呱谱模式：新规则
        UpdateStatus(L"使用延迟: " + to_wstring(delayMs) + L" 毫秒 (呱呱谱)");

        while (!stopPlayback && file.get(c)) {
            unique_lock<mutex> lock(fileMutex);
            cv.wait(lock, [&] { return !isPaused || stopPlayback; });
            if (stopPlayback) break;

            // 遇到 '#' 或中文，跳过本行
            if (c == L'#' || (c >= L'\u4E00' && c <= L'\u9FFF')) {
                while (file.get(c) && c != L'\n');
                continue;
            }

            if (iswupper(c)) {
                // 字母直接按键，无延迟
                BYTE vk = (BYTE)c;
                BYTE scan = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
                keybd_event(vk, scan, 0, 0);
                keybd_event(vk, scan, KEYEVENTF_KEYUP, 0);

                // 处理字母后的连续+
                int consecutivePlus = 0;
                while (!stopPlayback && file.peek() == L'+') {
                    file.get(c);
                    consecutivePlus++;
                    if (consecutivePlus > 20) {
                        stopPlayback = true;
                        UpdateStatus(L"连续超过20个+，停止播放。");
                        break;
                    }
                    this_thread::sleep_for(chrono::milliseconds(4 * delayMs));
                }
                if (stopPlayback) break;
            }
            else if (c == L'=') {
                this_thread::sleep_for(chrono::milliseconds(delayMs));
            }
            else if (c == L'-') {
                this_thread::sleep_for(chrono::milliseconds(2 * delayMs));
            }
            else if (c == L'+') {
                this_thread::sleep_for(chrono::milliseconds(4 * delayMs));
            }
            // 其他字符忽略（包括\n, / 等）
        }
    }

    file.close();
    UpdateStatus(L"播放完成。");
    isPlaying = false;
    SetWindowText(hBtnStartStop, L"开始");
}

void ControlProc() {
    while (true) {
        if (GetAsyncKeyState(VK_PRIOR) & 0x8000) {  // PgUp键: 选择文件
            SendMessage(hWndMain, WM_COMMAND, 1, 0);
            this_thread::sleep_for(chrono::milliseconds(200));
        }
        if (GetAsyncKeyState(VK_END) & 0x8000) {  // End键: 开始/停止
            SendMessage(hWndMain, WM_COMMAND, 2, 0);
            this_thread::sleep_for(chrono::milliseconds(200));
        }
        if (GetAsyncKeyState(VK_HOME) & 0x8000) {  // Home键: 暂停/继续
            SendMessage(hWndMain, WM_COMMAND, 3, 0);
            this_thread::sleep_for(chrono::milliseconds(200));
        }
        this_thread::sleep_for(chrono::milliseconds(10));
    }
}

void UpdateStatus(const wstring& msg) {
    wstring currentText;
    int len = GetWindowTextLength(hEditStatus) + 1;
    currentText.resize(static_cast<size_t>(len));
    GetWindowText(hEditStatus, &currentText[0], len);
    currentText = currentText.substr(0, currentText.size() - 1);
    currentText += msg + L"\r\n";
    SetWindowText(hEditStatus, currentText.c_str());

    // 修改滚动逻辑：直接滚动到文档底部，避免反向跳动
    PostMessage(hEditStatus, EM_SETSEL, (WPARAM)-1, (LPARAM)-1);  // 选择到文本末尾
    PostMessage(hEditStatus, EM_SCROLL, SB_BOTTOM, 0);            // 滚动到底部（文档末尾）
}

wstring GetExecutablePath() {
    wchar_t buffer[MAX_PATH] = { 0 };
    GetModuleFileNameW(NULL, buffer, MAX_PATH);
    return buffer;
}

bool IsUserAdmin() {
    BOOL isAdmin = FALSE;
    SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY;
    PSID adminGroup = nullptr;
    if (AllocateAndInitializeSid(&ntAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &adminGroup)) {
        CheckTokenMembership(NULL, adminGroup, &isAdmin);
        FreeSid(adminGroup);
    }
    return isAdmin;
}

bool RunAsAdmin(const wstring& executablePath) {
    SHELLEXECUTEINFO shellInfo = { sizeof(shellInfo) };
    shellInfo.lpVerb = L"runas";
    shellInfo.lpFile = executablePath.c_str();
    shellInfo.nShow = SW_NORMAL;
    return ShellExecuteEx(&shellInfo);
}